﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: ViewDefParameterViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
using VmroleAuthor.Commands;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class ViewDefParameterViewModel : ViewModelBase, IDataErrorInfo, IContainOsVhdTags, ICleanupParamsAndPayload
    {
        private readonly ViewDefParameter viewDefParameter;
        private readonly ViewDefCategoryViewModel viewDefCategoryViewModel;
        private readonly ViewDefViewModel viewDefViewModel;
        private ObservableCollection<OsVhdTagViewModel> imageTags;
        private ObservableCollection<ViewDefOptionValueViewModel> options;
        private string newTagToBeAdded;       
        private bool isExpanded;

        //null if unavailable
        private readonly string originalUnlocalizedLabel;

        private ViewDefParameterLocalizablePropertyViewModel label;
        private ViewDefParameterLocalizablePropertyViewModel description;
        private ViewDefParameterLocalizablePropertyViewModel defaultValue;

        private ViewDefParameterLocalizablePropertyViewModel validationMessageRequired;
        private ViewDefParameterLocalizablePropertyViewModel validationMessageLength;
        private ViewDefParameterLocalizablePropertyViewModel validationMessageRegEx;
        private ViewDefParameterLocalizablePropertyViewModel validationMessageRange;

        public static class PropertyNames
        {
            public const string Name = "Name";
            public const string Type = "Type";
            public const string Label = "Label";
            
            public const string Description = "Description";
            public const string DefaultValue = "DefaultValue";
            public const string Configurable = "Configurable";

            public const string NewTagToBeAdded = "NewTagToBeAdded";
            public const string ImageTags = "ImageTags";
         
            public const string Options = "Options";

            public const string ValidationRequired = "ValidationRequired";
            public const string ValidationMinLength = "ValidationMinLength";
            public const string ValidationMaxLength = "ValidationMaxLength";
            public const string ValidationRegExp = "ValidationRegExp";
            public const string ValidationMinRange = "ValidationMinRange";
            public const string ValidationMaxRange = "ValidationMaxRange";

            public const string ValidationMessageRequired = "ValidationMessageRequired";
            public const string ValidationMessageLength = "ValidationMessageLength";
            public const string ValidationMessageRegExp = "ValidationMessageRegExp";
            public const string ValidationMessageRange = "ValidationMessageRange";
        }

        public ViewDefParameterViewModel(
            ViewDefParameter viewDefParameter,
            ViewDefCategoryViewModel viewDefCategoryViewModel,
            ViewDefViewModel viewDefViewModel,
            bool autoLocalizeSomeProperties
            )
        {
            this.viewDefParameter = viewDefParameter;
            this.viewDefCategoryViewModel = viewDefCategoryViewModel;
            Debug.Assert(this.viewDefParameter != null);
            this.viewDefViewModel = viewDefViewModel;
            Debug.Assert(this.viewDefViewModel != null);

            this.imageTags = new ObservableCollection<OsVhdTagViewModel>();
            this.options = new ObservableCollection<ViewDefOptionValueViewModel>();
            ViewDefParameterType paramType;
            EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
            if (paramType == ViewDefParameterType.OSVirtualHardDisk &&
                this.viewDefParameter.ImageTags != null)
            {
                foreach (var tag in this.viewDefParameter.ImageTags)
                {
                    this.imageTags.Add(new OsVhdTagViewModel(tag, this));
                }
            }
            else if (paramType == ViewDefParameterType.Option && this.viewDefParameter.OptionValues != null)
            {
                if (this.viewDefParameter.OptionValues == null)
                {
                    this.viewDefParameter.OptionValues = new List<OptionValue>();
                }
                foreach (var op in this.viewDefParameter.OptionValues)
                {
                    if (op != null)
                    {
                        this.options.Add(new ViewDefOptionValueViewModel(op, this.viewDefViewModel));
                    }                   
                }
            }

            this.AddOsVhdTagCommand = new RelayCommand(
              param => this.AddTag((string)param),
              param => this.CanAddTag((string)param));

            this.AddOptionCommand = new RelayCommand(
             param => this.AddOption(),
             param => this.CanAddOption());

            this.RemoveOptionCommand = new RelayCommand(
            param => this.RemoveOption((ViewDefOptionValueViewModel)param),
            param => this.CanRemoveOption((ViewDefOptionValueViewModel)param));

            this.RemoveViewDefParameterCommand = new RelayCommand(
                param => this.RemoveViewDefParameter(),
                param => this.CanRemoveViewDefParameter());

            this.MoveViewDefParameterUpCommand = new RelayCommand(
                param => this.MoveViewDefParameterUp(),
                param => this.CanMoveViewDefParameterUp());

            this.MoveViewDefParameterDownCommand = new RelayCommand(
                param => this.MoveViewDefParameterDown(),
                param => this.CanMoveViewDefParameterDown());          
        
            if (!ViewDefLocalizationHelper.IsPropertyLocalized(this.viewDefParameter.Label))
            {
                this.originalUnlocalizedLabel = this.viewDefParameter.Label;
            }
            

            if (autoLocalizeSomeProperties)
            {
                this.Label.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
                this.Description.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
                this.ValidationMessageRequired.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
            }
        }

        public ViewDefParameter ViewDefParameter
        {
            get { return this.viewDefParameter; }
        }

        public string Name
        {
            get { return this.viewDefParameter.Name;  }
            set
            {
                this.viewDefParameter.Name = value;
                this.OnPropertyChanged(PropertyNames.Name);
            }
        }       

    
        public ICommand RemoveViewDefParameterCommand
        {
            get;
            private set;
        }

        public bool CanRemoveViewDefParameter()
        {
            return true;
        }

        public void RemoveViewDefParameter()
        {
            this.viewDefCategoryViewModel.ViewDefCategory.Parameters.Remove(this.viewDefParameter);           
            this.viewDefCategoryViewModel.ViewDefParameterViewModels.Remove(this);
            this.Cleanup();
        }

        public ICommand MoveViewDefParameterUpCommand
        {
            get; 
            private set; 
        }

        public bool CanMoveViewDefParameterUp()
        {
            return this.viewDefCategoryViewModel.CanMoveViewDefParameterUp(this);
        }

        public void MoveViewDefParameterUp()
        {
           this.viewDefCategoryViewModel.MoveViewDefParameterUp(this);
        }

        public ICommand MoveViewDefParameterDownCommand
        {
            get;
            private set;
        }

        public bool CanMoveViewDefParameterDown()
        {
            return this.viewDefCategoryViewModel.CanMoveViewDefParameterDown(this);
        }

        public void MoveViewDefParameterDown()
        {
            this.viewDefCategoryViewModel.MoveViewDefParameterDown(this);
        }

        public List<string> AvailableParameterTypes
        {
            get
            {
                List<string> avl = new List<string>(Enum.GetNames(typeof(ViewDefParameterType)));             
                return avl;
            }
        }

        public string Type
        {
            get { return this.viewDefParameter.Type; }
            set
            {
                if (value != null && value != this.viewDefParameter.Type)
                {                   
                    ViewDefParameterType paramType;
                    if (EnumHelper.TryParse(value, true, out paramType))
                    {
                        //clear old
                        if (this.viewDefParameter.Validation != null)
                        {
                            this.viewDefParameter.Validation.MinLength = null;
                            this.viewDefParameter.Validation.MaxLength = null;
                            this.viewDefParameter.Validation.MinRange = null;
                            this.viewDefParameter.Validation.MaxRange = null;
                            this.viewDefParameter.Validation.RegExp = null;
                        }
                        this.ValidationMessageRegExp.PropertyValue = null;
                        this.ValidationMessageLength.PropertyValue = null;
                        this.ValidationMessageRegExp.PropertyValue = null;
                        this.ValidationMessageRange.PropertyValue = null;
                        this.viewDefParameter.ImageTags = null;
                        this.viewDefParameter.OptionValues = null;
                        if (this.options != null)
                        {
                            this.options.Clear();
                        }
                        if (this.imageTags != null)
                        {
                            this.imageTags.Clear();
                        }                        
                        // Initialize...
                        switch (paramType)
                        {
                            case ViewDefParameterType.String:
                            case ViewDefParameterType.ComputerNamePattern:
                            case ViewDefParameterType.SecureString:
                                this.ValidationMinLength = 0;
                                this.ValidationMaxLength = 0;
                                this.ValidationMessageRegExp.PropertyValue = null;
                                this.ValidationMessageLength.PropertyValue = null;
                                this.ValidationMessageRegExp.PropertyValue = null;
                                break;
                            case ViewDefParameterType.Number:
                                this.ValidationMaxRange = 0;
                                this.ValidationMinRange = 0;
                                this.ValidationMessageRange.PropertyValue = null;
                                break;
                            case ViewDefParameterType.Option:
                                this.viewDefParameter.OptionValues = new List<OptionValue>();
                                if (this.options == null)
                                {
                                    this.options = new ObservableCollection<ViewDefOptionValueViewModel>();
                                }
                                else
                                {
                                    this.options.Clear();
                                }                                
                                break;
                            case ViewDefParameterType.OSVirtualHardDisk:
                                this.viewDefParameter.ImageTags = new List<string>();
                                if (this.imageTags == null)
                                {
                                    this.imageTags = new ObservableCollection<OsVhdTagViewModel>();
                                }
                                else
                                {
                                    this.imageTags.Clear();
                                }                                
                                break;
                        }
                        this.viewDefParameter.Type = value;                 
                    }
                   
                    
                }
                
                this.OnPropertyChanged(PropertyNames.Type); 
            }

        }

        public ViewDefParameterLocalizablePropertyViewModel Label
        {
            get
            {
                if (this.label == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefParameter.Label;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.Label = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.Label;
                    Func<string> someInitialValue = () => this.originalUnlocalizedLabel ?? this.Name;                  

                    this.label = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.Label,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,                                            
                        this.viewDefViewModel,
                        true,
                        someInitialValue); 
                }
                return this.label;
            }
        }

        public ViewDefParameterLocalizablePropertyViewModel DefaultValue
        {
            get
            {
                if (this.defaultValue == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefParameter.DefaultValue;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.DefaultValue = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.DefaultValue;

                    Func<string> someInitialValue = () =>
                        {
                            string defaultLocalizedValue =
                                ViewDefLocalizationHelper.IsPropertyLocalized(this.viewDefParameter.DefaultValue)
                                    ? null
                                    : this.viewDefParameter.DefaultValue;
                            if (defaultLocalizedValue == null)
                            {
                                ViewDefParameterType paramType;
                                EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
                                switch (paramType)
                                {
                                    case ViewDefParameterType.ComputerNamePattern:
                                        defaultLocalizedValue = "MyMachine###";
                                        break;
                                    case ViewDefParameterType.String:
                                        defaultLocalizedValue = "SomeDefaultString";
                                        break;
                                    case ViewDefParameterType.Number:
                                        defaultLocalizedValue = "0";
                                        break;
                                    case ViewDefParameterType.Boolean:
                                        defaultLocalizedValue = "true";
                                        break;
                                    default:
                                        defaultLocalizedValue = null;
                                        break;
                                }
                            }
                            return defaultLocalizedValue;
                        };

                    
                    

                    this.defaultValue = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.DefaultValue,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        false,
                        someInitialValue);
                }
                return this.defaultValue;
            }
        }

        public ViewDefParameterLocalizablePropertyViewModel Description
        {
            get
            {
                if (this.description == null)
                {
                    Func<string> paramValueGetter = () => this.viewDefParameter.Description;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.Description = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.Description;
                    Func<string> defaultLocalizedValue = () => ViewDefLocalizationHelper.IsPropertyLocalized(this.viewDefParameter.Description) ? this.Name : this.viewDefParameter.Description; 

                    this.description = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.Description,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        true,
                        defaultLocalizedValue);
                }
                return this.description;
            }
        }


        public bool Configurable
        {
            get
            {
                return this.viewDefParameter.Configurable;
            }
            set
            {
                this.viewDefParameter.Configurable = value;
                this.OnPropertyChanged(PropertyNames.Configurable);
            }
        }

        public bool IsExpanded
        {
            get { return this.isExpanded; }
            set 
            { 
                this.isExpanded = value;
                this.OnPropertyChanged("IsExpanded");
            }
        }

        #region Image Tags - OS VHD

        private List<string> availableTags;
        public List<string> AvailableTags
        {
            get
            {
                if (this.availableTags == null)
                {
                    //lazy
                    this.availableTags = new List<string>();
                    this.availableTags.AddRange(RecommendedTags.GeneralTags);
                    this.availableTags.AddRange(RecommendedTags.MajorVersionTags);
                    this.availableTags.AddRange(RecommendedTags.ArchitectureTags);
                    this.availableTags.AddRange(RecommendedTags.ReleaseRelatedTags);
                    this.availableTags.AddRange(RecommendedTags.ReleaseModifierTags);
                    this.availableTags.AddRange(RecommendedTags.SQLServerTags);
                    this.availableTags.AddRange(RecommendedTags.PrerequistiesTags);
                }
                return this.availableTags;
            }
        }      

        public string NewTagToBeAdded
        {
            get { return this.newTagToBeAdded; }
            set
            {
                this.newTagToBeAdded = value;
                this.OnPropertyChanged(PropertyNames.NewTagToBeAdded);
            }
        }

        public ICommand AddOsVhdTagCommand
        {
            get;
            private set;
        }

        public bool CanAddTag(string tag)
        {
            ViewDefParameterType paramType;
            EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
            return !string.IsNullOrEmpty(tag) && paramType == ViewDefParameterType.OSVirtualHardDisk &&
                this.imageTags.FirstOrDefault(x => string.Equals(x.Tag, tag, StringComparison.Ordinal)) == null;
        }

        public bool CanRemoveTag(string tag)
        {
            return tag != null && this.imageTags.First(x => string.Equals(x.Tag, tag, StringComparison.Ordinal)) != null;
        }

        public void AddTag(string tag)
        {
            ViewDefParameterType paramType;
            EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
            if (paramType == ViewDefParameterType.OSVirtualHardDisk)
            {
                this.imageTags.Add(new OsVhdTagViewModel(tag, this));
                this.viewDefParameter.ImageTags = this.imageTags.Select(vhdTag => vhdTag.Tag).ToList();
                this.OnPropertyChanged(PropertyNames.ImageTags);
                this.NewTagToBeAdded = string.Empty;
            }          
        }

        public void RemoveTag(string tag)
        {
            var osVhdTag = this.imageTags.First(x => string.Equals(x.Tag, tag, StringComparison.OrdinalIgnoreCase));
            if (osVhdTag != null)
            {
                this.imageTags.Remove(osVhdTag);
                this.viewDefParameter.ImageTags = this.imageTags.Select(vhdTag => vhdTag.Tag).ToList();                
            }
            if (this.viewDefParameter.ImageTags != null &&
                this.viewDefParameter.ImageTags.Contains(tag, StringComparer.Ordinal))
            {
                this.viewDefParameter.ImageTags.Remove(tag);
            }
            this.OnPropertyChanged(PropertyNames.ImageTags);              
        }

        public ObservableCollection<OsVhdTagViewModel> ImageTags
        {
            get { return this.imageTags; }
        }

        #endregion Image Tags - OS VHD



        #region Options
     
        public ObservableCollection<ViewDefOptionValueViewModel> Options
        {
            get
            {
                return this.options;              
            }
        }       

        public ICommand AddOptionCommand
        {
            get;
            private set;
        }

        public bool CanAddOption()
        {
            ViewDefParameterType paramType;
            EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
            return (paramType == ViewDefParameterType.Option);          
        }

        public ICommand RemoveOptionCommand
        {
            get;
            private set;
        }
             
        public void AddOption()
        {
            ViewDefParameterType paramType;
            EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
            if (paramType == ViewDefParameterType.Option)
            {
                if (this.viewDefParameter.OptionValues == null)
                {
                    this.viewDefParameter.OptionValues = new List<OptionValue>();
                }
                OptionValue op = new OptionValue();
                ViewDefOptionValueViewModel optionValueViewModel = new ViewDefOptionValueViewModel(op, this.viewDefViewModel);
                this.viewDefParameter.OptionValues.Add(op); 
                this.options.Add(optionValueViewModel);
                this.OnPropertyChanged(PropertyNames.Options);
            }
        }

        public bool CanRemoveOption(ViewDefOptionValueViewModel optionValueViewModel)
        {
            return optionValueViewModel != null ;
        }


        public void RemoveOption(ViewDefOptionValueViewModel optionValueViewModel)
        {
            if (optionValueViewModel != null)
            {
                this.viewDefParameter.OptionValues.Remove(optionValueViewModel.Option);               
                this.options.Remove(optionValueViewModel);
            }          
            this.OnPropertyChanged(PropertyNames.Options);
        }

       

        #endregion Options

        /// <summary>
        /// For all viewdef parameter types
        /// </summary>
        public bool ValidationRequired
        {
            get { return this.viewDefParameter.Validation != null && this.viewDefParameter.Validation.Required; }
            set
            {
                if (this.viewDefParameter.Validation == null)
                {
                    this.viewDefParameter.Validation = new ViewDefParameterValidation();
                }
                this.viewDefParameter.Validation.Required = value;
                this.OnPropertyChanged(PropertyNames.ValidationRequired);            
            }
        }

        /// <summary>
        /// Optional for StringParam
        /// </summary>
        public int ValidationMinLength
        {
            get
            {
                return this.viewDefParameter.Validation != null  ? this.viewDefParameter.Validation.MinLength.GetValueOrDefault() : 0;
            }
            set
            {
                if (this.viewDefParameter.Validation == null)
                {
                    this.viewDefParameter.Validation = new ViewDefParameterValidation();
                }
                this.viewDefParameter.Validation.MinLength = value;
                this.OnPropertyChanged(PropertyNames.ValidationMinLength);
            }
        }

        /// <summary>
        /// Optional for StringParam
        /// </summary>
        public int ValidationMaxLength
        {
            get
            {
                return this.viewDefParameter.Validation != null ? this.viewDefParameter.Validation.MaxLength.GetValueOrDefault() : int.MaxValue;
            }
            set
            {
                if (this.viewDefParameter.Validation == null)
                {
                    this.viewDefParameter.Validation = new ViewDefParameterValidation();
                }
                this.viewDefParameter.Validation.MaxLength = value;
                this.OnPropertyChanged(PropertyNames.ValidationMaxLength);
            }
        }

        /// <summary>
        /// Optional for StringParam
        /// </summary>
        public string ValidationRegExp
        {
            get
            {
                return this.viewDefParameter.Validation != null ? this.viewDefParameter.Validation.RegExp : null;
            }
            set
            {
                if (this.viewDefParameter.Validation == null)
                {
                    this.viewDefParameter.Validation = new ViewDefParameterValidation();
                }
                this.viewDefParameter.Validation.RegExp = value;
                this.OnPropertyChanged(PropertyNames.ValidationRegExp);
            }
        }

        /// <summary>
        /// Optional for NumParam
        /// </summary>
        public int ValidationMinRange
        {
            get
            {
                return this.viewDefParameter.Validation != null ? this.viewDefParameter.Validation.MinRange.GetValueOrDefault() : 0;
            }
            set
            {
                if (this.viewDefParameter.Validation == null)
                {
                    this.viewDefParameter.Validation = new ViewDefParameterValidation();
                }
                this.viewDefParameter.Validation.MinRange = value;
                this.OnPropertyChanged(PropertyNames.ValidationMinLength);
            }
        }

        /// <summary>
        /// Optional for NumParam
        /// </summary>
        public int ValidationMaxRange
        {
            get
            {
                return this.viewDefParameter.Validation != null ? this.viewDefParameter.Validation.MaxRange.GetValueOrDefault() : int.MaxValue;
            }
            set
            {
                if (this.viewDefParameter.Validation == null)
                {
                    this.viewDefParameter.Validation = new ViewDefParameterValidation();
                }
                this.viewDefParameter.Validation.MaxRange = value;
                this.OnPropertyChanged(PropertyNames.ValidationMaxRange);
            }
        }                 

        /// <summary>
        /// Required for all
        /// </summary>
        public ViewDefParameterLocalizablePropertyViewModel ValidationMessageRequired
        {
            get
            {
                if (this.validationMessageRequired == null)
                {
                    if (this.viewDefParameter.Validation == null)
                    {
                        this.viewDefParameter.Validation = new ViewDefParameterValidation();                      
                    }
                    if (this.viewDefParameter.Validation.Messages == null)
                    {
                        this.viewDefParameter.Validation.Messages = new ViewDefParameterValidationMessages();
                    }

                    Func<string> paramValueGetter = () => this.viewDefParameter.Validation.Messages.Required;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.Validation.Messages.Required = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.ValidationMessageRequired;
                    string prefix = this.originalUnlocalizedLabel ?? this.Name;
                    Func<string> defaultLocalizedValue = () =>  string.Format("{0} is required", prefix);

                    this.validationMessageRequired = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.ValidationMessageRequired,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        true,
                        defaultLocalizedValue);
                }
                return this.validationMessageRequired;
            }
        }

        /// <summary>
        /// Optional for StringParam
        /// </summary>
        public ViewDefParameterLocalizablePropertyViewModel ValidationMessageLength
        {
            get
            {
                if (this.validationMessageLength == null)
                {
                    if (this.viewDefParameter.Validation == null)
                    {
                        this.viewDefParameter.Validation = new ViewDefParameterValidation();
                    }
                    if (this.viewDefParameter.Validation.Messages == null)
                    {
                        this.viewDefParameter.Validation.Messages = new ViewDefParameterValidationMessages();
                    }

                    Func<string> paramValueGetter = () => this.viewDefParameter.Validation.Messages.Length;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.Validation.Messages.Length = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.ValidationMessageLength;
                    string prefix = this.originalUnlocalizedLabel ?? this.Name;
                    Func<string> defaultLocalizedValue = () => string.Format("{0} should be between {1} to {2} characters", prefix, this.ValidationMinLength, this.ValidationMaxLength);

                    this.validationMessageLength = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.ValidationMessageLength,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        false,
                        defaultLocalizedValue);
                }
                return this.validationMessageLength;
            }
        }

        /// <summary>
        /// Optional for StringParam
        /// </summary>
        public ViewDefParameterLocalizablePropertyViewModel ValidationMessageRegExp
        {
            get
            {
                if (this.validationMessageRegEx == null)
                {
                    if (this.viewDefParameter.Validation == null)
                    {
                        this.viewDefParameter.Validation = new ViewDefParameterValidation();
                    }
                    if (this.viewDefParameter.Validation.Messages == null)
                    {
                        this.viewDefParameter.Validation.Messages = new ViewDefParameterValidationMessages();
                    }

                    Func<string> paramValueGetter = () => this.viewDefParameter.Validation.Messages.RegExp;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.Validation.Messages.RegExp = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.ValidationMessageRegExp;
                    string prefix = this.originalUnlocalizedLabel ?? this.Name;
                    Func<string> defaultLocalizedValue = () => string.Format("{0} is not valid", prefix);

                    this.validationMessageRegEx = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.ValidationMessageRegExp,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        false,
                        defaultLocalizedValue);
                }
                return this.validationMessageRegEx;
            }
        }

        /// <summary>
        /// Optional for NumParam
        /// </summary>
        public ViewDefParameterLocalizablePropertyViewModel ValidationMessageRange
        {
            get
            {
                if (this.validationMessageRange == null)
                {
                    if (this.viewDefParameter.Validation == null)
                    {
                        this.viewDefParameter.Validation = new ViewDefParameterValidation();
                    }
                    if (this.viewDefParameter.Validation.Messages == null)
                    {
                        this.viewDefParameter.Validation.Messages = new ViewDefParameterValidationMessages();
                    }

                    Func<string> paramValueGetter = () => this.viewDefParameter.Validation.Messages.Range;
                    Action<string> paramValueSetter = delegate(string value) { this.viewDefParameter.Validation.Messages.Range = value; };
                    Func<string> autoParamNameGetter = () => this.viewDefParameter.Name + PropertyNames.ValidationMessageRange;
                    string prefix = this.originalUnlocalizedLabel ?? this.Name;
                    Func<string> defaultLocalizedValue = () => string.Format("{0} should be between {1} and {2}", prefix, this.ValidationMinRange, this.ValidationMaxRange);

                    this.validationMessageRange = new ViewDefParameterLocalizablePropertyViewModel(
                        PropertyNames.ValidationMessageRange,
                        paramValueGetter,
                        paramValueSetter,
                        autoParamNameGetter,
                        this.viewDefViewModel,
                        false,
                        defaultLocalizedValue);
                }
                return this.validationMessageRange;
            }
        }

        public void Cleanup()
        {          
            List<string> propNames = ParametersAndPayloadCleanupHelper.GetPropertyNames(typeof(PropertyNames));
            foreach (string propName in propNames)
            {
                // Clean up child properties
                object propValue = this.GetType().GetProperty(propName).GetValue(this);
                ParametersAndPayloadCleanupHelper.CleanupParametersAndPayloads(propValue);
            }
        }
       

        public string this[string propertyName]
        {
            get
            {
                string error = null;
                switch (propertyName)
                {
                    case PropertyNames.Label:
                        this.IsExpanded = true;
                        break;
                    case PropertyNames.NewTagToBeAdded:
                        ViewDefParameterType paramType;
                        EnumHelper.TryParse(this.viewDefParameter.Type, true, out paramType);
                        if (paramType == ViewDefParameterType.OSVirtualHardDisk && !string.IsNullOrEmpty(this.NewTagToBeAdded))
                        {
                            if (ParameterizationHelper.IsFullyParameterized(this.NewTagToBeAdded))
                            {
                                error = "Cannot parameterize tags";
                            }
                            if (error == null)
                            {                              
                                error = ValidationHelper.ValidateTags(this.NewTagToBeAdded, this.viewDefParameter.ImageTags);
                            }
                        }
                        break;
                }
                return error;               
            }
        }

        public string Error { get; set; }        
    }
}
